package yartiss.engine.simulation.scheduling.policies.fp;

import java.util.List;
import java.util.SortedSet;

import yartiss.engine.simulation.Event;
import yartiss.engine.simulation.IEnergyProfile;
import yartiss.engine.simulation.ISchedulingPolicy;
import yartiss.engine.simulation.events.EventGenerator;
import yartiss.engine.simulation.scheduling.policies.AbstractMonoProcessorSchedulingPolicy;
import yartiss.engine.simulation.scheduling.policies.tools.FPSlackTimeManager;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;
import yartiss.engine.simulation.system.taskset.TaskSetFactory;

/**
 * Preemptive Fixed Priority. Two threshold are setted : Etmin and Etmax.
 * When energy fail under Etmin, the system is paused for a maximal duration
 * equal to the slack time. If energy state reaches Etmax, the schedule is
 * resumed.
 */
public class EHPFP5ScheculingPolicy extends AbstractMonoProcessorSchedulingPolicy{

	private List<Integer> parameters;
	private final FPSlackTimeManager slackTimeManager;


	public EHPFP5ScheculingPolicy(FPSlackTimeManager slackTimeManager){
		this.slackTimeManager = slackTimeManager;
	}

	@Override
	public String getPolicyName() {
		return "EH_PFP_5";
	}

	@Override
	public void setParameters(List<Integer> parameters) {
		this.parameters = parameters;
	}

	@Override
	public List<Integer> getParameters() {
		return parameters;
	}

	@Override
	public ITaskSet createTaskSet() {
		return TaskSetFactory.getNewInstance("priority");
	}
	
	@Override
	public ISchedulingPolicy newInstance() {
		return new EHPFP5ScheculingPolicy(new FPSlackTimeManager());
	}

	boolean isPaused = false;

	@Override
	public ITask chooseNextTask(ITaskSet taskSet, IEnergyProfile energyProfile, long date, EventGenerator evGen) {
		metrics.clear();
		if (date == 0)
			isPaused = false;
		
		
		long emin = this.parameters.isEmpty() ? energyProfile.getMinEnergyLevel()	: 
			(energyProfile.getMaxEnergyLevel() - energyProfile.getMinEnergyLevel())	* parameters.get(0)	/ 100+ energyProfile.getMinEnergyLevel();

		long emax = this.parameters.isEmpty() ? energyProfile.getMaxEnergyLevel()	: 
			(energyProfile.getMaxEnergyLevel() - energyProfile.getMinEnergyLevel())	* parameters.get(1)	/ 100	+ energyProfile.getMinEnergyLevel();

		assert energyProfile.getMinEnergyLevel() <= emin && emin < emax && emax <= energyProfile.getMaxEnergyLevel() : "you should have Emin <= th_min < th_max <= Emax";

				long e = energyProfile.getCurrentEnergyLevel();

				SortedSet<ITask> activeTasks = taskSet.getActiveTasks(date);

				if (!activeTasks.isEmpty()) {
					ITask first = activeTasks.first();
					if (isPaused) {

						if (e >= emax) {

							evGen.generateEvent("check_energy_state", activeTasks.first(), date, null);
							isPaused = false;
							return null;

						}

						// e < emax

						long st = slackTimeManager.getSlackTime(date, taskSet);
						metrics.put(Event.SlackTime, st);
						if (st <= 0) {
							evGen.generateEvent("check_energy_state", activeTasks.first(), date, null);
							isPaused = false;
							return null;
						}

						// e < emax && st > 0
						long wakeUp = Math.min(date + st, date	+ (emax - e)	/ energyProfile.getPowerRate());

						if (wakeUp == date)
							wakeUp++;
						evGen.generateEvent("check_energy_state", activeTasks.first(), wakeUp, null);

						return null;

					}

					if (e <= emin) {

						long st = slackTimeManager.getSlackTime(date, taskSet);
						metrics.put(Event.SlackTime, st);
						if (st > 0) {

							// not paused, e <= emin , st > 0

							isPaused = true;

							long wakeUp = Math.min(date	+ st, date	+ (emax - e)/ energyProfile.getPowerRate());
							if (wakeUp == date)
								wakeUp++;
							evGen.generateEvent("check_energy_state", activeTasks.first(), wakeUp, null);

							return null;
						}

						// not paused, e <= emin , st <= 0

						long hlcet = energyProfile.howLongCanExecute(first);
						if (hlcet > 0) {
							if (hlcet < activeTasks.first().getRemainingCost())
								evGen.generateEvent("check_energy_state", activeTasks.first(), date+ hlcet, null);
							return activeTasks.first();
						}

						evGen.generateEvent("check_energy_state", activeTasks.first(), date + 1, null);
						// => de toutes facon dlm, mais peut etre pas la premiere
						// tache
						return null;
					}

					// not paused, emin < e

					long hlcet = energyProfile.howLongCanExecute(e - emin,first);

					if (hlcet > 0) {
						if (hlcet < activeTasks.first().getRemainingCost())
							evGen.generateEvent("check_energy_state", activeTasks.first(), date+ hlcet, null);
						return activeTasks.first();
					} else {
						if (e < emax) {
							isPaused = true;
							long wait = slackTimeManager.getSlackTime(date, taskSet);
							metrics.put(Event.SlackTime, wait);
							wait = Math.max(1, wait);
							wait = Math.min(wait,	(emax - e)/ energyProfile.getPowerRate());
							if (wait == 0)
								wait = 1;
							evGen.generateEvent("check_energy_state", activeTasks.first(), date+ wait, null);
						} else
							evGen.generateEvent("check_energy_state", activeTasks.first(), date + 1, null);
					}
				}
				return null;
	}
}
